/**
 *  LICENSE AND TRADEMARK NOTICES
 *  
 *  Except where noted, sample source code written by Motorola Mobility Inc. and
 *  provided to you is licensed as described below.
 *  
 *  Copyright (c) 2012, Motorola, Inc.
 *  All  rights reserved except as otherwise explicitly indicated.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 *  - Neither the name of Motorola, Inc. nor the names of its contributors may
 *  be used to endorse or promote products derived from this software without
 *  specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *  
 *  Other source code displayed may be licensed under Apache License, Version
 *  2.
 *  
 *  Copyright 卢漏 2012, Android Open Source Project. All rights reserved unless
 *  otherwise explicitly indicated.
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not
 *  use this file except in compliance with the License. You may obtain a copy
 *  of the License at
 *  
 *  http://www.apache.org/licenses/LICENSE-2.0.
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 *  License for the specific language governing permissions and limitations
 *  under the License.
 *  
 */

// Please refer to the accompanying article at 
// http://developer.motorola.com/docs/using_the_advanced_encryption_standard_in_android/

package com.limao.Tool;

import java.util.Date;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;


public class AES {
	
	private final String KEY_GENERATION_ALG = "PBKDF2WithHmacSHA1";

	private final int HASH_ITERATIONS = 100;
	private final int KEY_LENGTH = 256;
	
	private byte[] salt = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD,	0xE, 0xF }; // must save this for next time we want the key
	private final String CIPHERMODEPADDING = "AES/CBC/PKCS7Padding";


	//绠楁硶鍙傛暟 
	private byte[] iv = { 0xA, 1, 0xB, 5, 4, 0xF, 7, 9, 0x17, 3, 1, 6, 8, 0xC,0xD, 91 };
	private IvParameterSpec IV=new IvParameterSpec(iv);
   
	private static AES instance;

    public static AES getInstance() {
    	if(instance==null)    		instance=new  AES();
        return instance;
    }
	
	private AES() {	}

	private String numToAbc(String s)
	{
		 char[] key=s.toCharArray();
		 for(int i=0;i<key.length;i++)
		 {
			 key[i]=(char) (key[i]+17);
		 }
		 return String.valueOf(key);
	}
	
	private String abcToNum(String s)
	{
		 char[] key=s.toCharArray();
		 for(int i=0;i<key.length;i++)
		 {
			 key[i]=(char) (key[i]-17);
		 }
		 return String.valueOf(key);
	}	
	
	//鍔犲瘑
	public synchronized String encrypt(String msg){
		//寰楀埌褰撳墠鏃堕棿
		long t=(new Date()).getTime();
		String E=numToAbc(t+"");
		//寰楀埌skforAES
		SecretKeySpec skforAES = null;
		char[] charPass=("GLK"+t+"").toCharArray();
		try {
			PBEKeySpec myKeyspec = new PBEKeySpec(charPass, salt,	HASH_ITERATIONS, KEY_LENGTH);
			SecretKeyFactory keyfactory = SecretKeyFactory.getInstance(KEY_GENERATION_ALG);
			SecretKey sk = keyfactory.generateSecret(myKeyspec);

			byte[] skAsByteArray = sk.getEncoded();
			skforAES = new SecretKeySpec(skAsByteArray, "AES");
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		if(skforAES==null)
			return null;
		
		//寰楀埌AES鍔犲瘑	
		byte[] ciphertext = null;
		try {
			byte[] msgByte= msg.getBytes("UTF-8");
			Cipher c = Cipher.getInstance(CIPHERMODEPADDING);
			c.init(Cipher.ENCRYPT_MODE, skforAES, IV);
			ciphertext= c.doFinal(msgByte);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		if(ciphertext==null){
			return null;
		}
		//寰楀埌BASE64鍔犲瘑
		String base64_ciphertext = Base64Encoder.encode(ciphertext);
		return base64_ciphertext+E;

	}

	
	//瑙ｅ瘑
	public synchronized String decrypt(String ciphertext_base64) {
		//寰楀埌鎷嗗垎
		String pa=ciphertext_base64.substring(ciphertext_base64.length()-13);
		String content=ciphertext_base64.substring(0,ciphertext_base64.length()-13);
		//寰楀埌skforAES
		SecretKeySpec skforAES = null;
		String t=abcToNum(pa);
		char[] charPass=("GLK"+t).toCharArray();
		try {
			PBEKeySpec myKeyspec = new PBEKeySpec(charPass, salt,	HASH_ITERATIONS, KEY_LENGTH);
			SecretKeyFactory keyfactory = SecretKeyFactory.getInstance(KEY_GENERATION_ALG);
			SecretKey sk = keyfactory.generateSecret(myKeyspec);
			byte[] skAsByteArray = sk.getEncoded();
			skforAES = new SecretKeySpec(skAsByteArray, "AES");
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		if(skforAES==null)
			return null;

		//寰楀埌BASE64瑙ｅ瘑
		byte[] s = Base64Decoder.decodeToBytes(content);
		//寰楀埌AES瑙ｅ瘑
		byte[] decrypted = null;
		try {
			Cipher c = Cipher.getInstance(CIPHERMODEPADDING);
			c.init(Cipher.DECRYPT_MODE, skforAES, IV);
			decrypted= c.doFinal(s);
			return new String(decrypted,"UTF-8");
		} catch (Exception e) {
			e.printStackTrace();
		}
        
		return null;
		
	}


/*	private byte[] encryptAES(byte[] msg) {
		try {
			Cipher c = Cipher.getInstance(CIPHERMODEPADDING);
			c.init(Cipher.ENCRYPT_MODE, skforAES, IV);
			return c.doFinal(msg);
		} catch (NoSuchAlgorithmException nsae) {
			Log.e("AESdemo", "no cipher getinstance support for " + CIPHERMODEPADDING);
		} catch (NoSuchPaddingException nspe) {
			Log.e("AESdemo", "no cipher getinstance support for padding " + CIPHERMODEPADDING);
		} catch (InvalidKeyException e) {
			Log.e("AESdemo", "invalid key exception");
		} catch (InvalidAlgorithmParameterException e) {
			Log.e("AESdemo", "invalid algorithm parameter exception");
		} catch (IllegalBlockSizeException e) {
			Log.e("AESdemo", "illegal block size exception");
		} catch (BadPaddingException e) {
			Log.e("AESdemo", "bad padding exception");
		}
		return null;
	}
*/
	
	
/*	private byte[] decryptAES(byte[] ciphertext) {
		try {
			Cipher c = Cipher.getInstance(CIPHERMODEPADDING);
			c.init(Cipher.DECRYPT_MODE, skforAES, IV);
			return c.doFinal(ciphertext);
		} catch (NoSuchAlgorithmException nsae) {
			Log.e("AESdemo", "no cipher getinstance support for " + CIPHERMODEPADDING);
		} catch (NoSuchPaddingException nspe) {
			Log.e("AESdemo", "no cipher getinstance support for padding " + CIPHERMODEPADDING);
		} catch (InvalidKeyException e) {
			Log.e("AESdemo", "invalid key exception");
		} catch (InvalidAlgorithmParameterException e) {
			Log.e("AESdemo", "invalid algorithm parameter exception");
		} catch (IllegalBlockSizeException e) {
			Log.e("AESdemo", "illegal block size exception");
		} catch (BadPaddingException e) {
			Log.e("AESdemo", "bad padding exception");
			e.printStackTrace();
		}
		return null;
	}
*/	
	
	// Use this method if you want to add the padding manually
	// AES deals with messages in blocks of 16 bytes.
	// This method looks at the length of the message, and adds bytes at the end
	// so that the entire message is a multiple of 16 bytes.
	// the padding is a series of bytes, each set to the total bytes added (a
	// number in range 1..16).
	private byte[] addPadding(byte[] plain) {
		byte plainpad[] = null;
		int shortage = 16 - (plain.length % 16);
		// if already an exact multiple of 16, need to add another block of 16
		// bytes
		if (shortage == 0)
			shortage = 16;

		// reallocate array bigger to be exact multiple, adding shortage bits.
		plainpad = new byte[plain.length + shortage];
		for (int i = 0; i < plain.length; i++) {
			plainpad[i] = plain[i];
		}
		for (int i = plain.length; i < plain.length + shortage; i++) {
			plainpad[i] = (byte) shortage;
		}
		return plainpad;
	}

	// Use this method if you want to remove the padding manually
	// This method removes the padding bytes
	private byte[] dropPadding(byte[] plainpad) {
		byte plain[] = null;
		int drop = plainpad[plainpad.length - 1]; // last byte gives number of
													// bytes to drop

		// reallocate array smaller, dropping the pad bytes.
		plain = new byte[plainpad.length - drop];
		for (int i = 0; i < plain.length; i++) {
			plain[i] = plainpad[i];
			plainpad[i] = 0; // don't keep a copy of the decrypt
		}
		return plain;
	}


}
